﻿using System;
using System.Collections.Generic;
using System.Text;

namespace SharpSoft.Geometries
{
    /// <summary>
    /// 曲线(使用N【N>=2】个固定点描述一条曲线)
    /// </summary>
    public class Curve
    {
        public Curve(Point2D[] points)
        {
            this.Points = new Point2DCollection();
            this.Points.Append(points);
        }
        public Point2DCollection Points { get; private set; }

        /// <summary>
        /// 获取插值点
        /// </summary>
        /// <param name="tension">拉力</param>
        /// <param name="continuity">连续性</param>
        /// <param name="bias">误差</param>
        /// <param name="steplength">步长</param>
        /// <returns></returns>
        public virtual List<Point2D> GetInterpolatedPoints(double tension, double continuity, double bias,double steplength)
        {
            var interpolatedPoints = new List<Point2D>();
            if (Points.Count < 3)
            {
                interpolatedPoints.AddRange(Points);
            }
            else if (Points.Count == 3)
            {
                var ps = GetInterpolatedPoints(Points[0], Points[0], Points[1], Points[2], tension, continuity, bias, steplength);
                interpolatedPoints.AddRange(ps);
                ps = GetInterpolatedPoints(Points[0], Points[1], Points[2], Points[2], tension, continuity, bias, steplength);
                interpolatedPoints.AddRange(ps);
            }
            else
            {
                for (int i = 0; i < Points.Count; i++)
                {
                    if (i == 0)
                    {
                        var point1 = this.Points[i];
                        var point2 = this.Points[i];
                        var point3 = this.Points[i + 1];
                        var point4 = this.Points[i + 2];

                        var ps = GetInterpolatedPoints(point1, point2, point3, point4, tension, continuity, bias, steplength);
                        interpolatedPoints.AddRange(ps);
                    }
                    else if (i + 2 < this.Points.Count)
                    {
                        var point1 = this.Points[i - 1];
                        var point2 = this.Points[i];
                        var point3 = this.Points[i + 1];
                        var point4 = this.Points[i + 2];

                        var ps = GetInterpolatedPoints(point1, point2, point3, point4, tension, continuity, bias, steplength);
                        interpolatedPoints.AddRange(ps);
                    }
                    else if (i + 1 < this.Points.Count)
                    {
                        var point1 = this.Points[i - 1];
                        var point2 = this.Points[i];
                        var point3 = this.Points[i + 1];
                        var point4 = this.Points[i + 1];

                        var ps = GetInterpolatedPoints(point1, point2, point3, point4, tension, continuity, bias, steplength);
                        interpolatedPoints.AddRange(ps);
                    }
                }
            }


            return interpolatedPoints;
        }
        private static List<Point2D> GetInterpolatedPoints(Point2D point1, Point2D point2, Point2D point3, Point2D point4, double tension, double continuity, double bias, double steplength)
        {
            var interpolatedPoints = new List<Point2D>();

            var v = point3 - point2;
            var vlength = v.Length;
            for (var i = 0d; i <= vlength; i += steplength)
            {//TODO:将使用steps均分改成使用steplength将每段分成等长的N段
                var s = i / vlength;

                var h1 = 2 * Math.Pow(s, 3) - 3 * Math.Pow(s, 2) + 1;
                var h2 = (-2) * Math.Pow(s, 3) + 3 * Math.Pow(s, 2);
                var h3 = Math.Pow(s, 3) - 2 * Math.Pow(s, 2) + s;
                var h4 = Math.Pow(s, 3) - Math.Pow(s, 2);

                var tDix = (1 - tension) * (1 + continuity) * (1 + bias) * (point2.X - point1.X) / 2 + (1 - tension) * (1 - continuity) * (1 - bias) * (point3.X - point2.X) / 2;
                var tDiy = (1 - tension) * (1 + continuity) * (1 + bias) * (point2.Y - point1.Y) / 2 + (1 - tension) * (1 - continuity) * (1 - bias) * (point3.Y - point2.Y) / 2;

                var tSix = (1 - tension) * (1 - continuity) * (1 + bias) * (point3.X - point2.X) / 2 + (1 - tension) * (1 + continuity) * (1 - bias) * (point4.X - point3.X) / 2;
                var tSiy = (1 - tension) * (1 - continuity) * (1 + bias) * (point3.Y - point2.Y) / 2 + (1 - tension) * (1 + continuity) * (1 - bias) * (point4.Y - point3.Y) / 2;

                var x = h1 * point2.X + h2 * point3.X + h3 * tDix + h4 * tSix;
                var y = h1 * point2.Y + h2 * point3.Y + h3 * tDiy + h4 * tSiy;

                interpolatedPoints.Add(new Point2D((int)x, (int)y));
            }

            interpolatedPoints.Add(point3);

            return interpolatedPoints;
        }
    }
}
